// 版权所有2016 etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

package raft

import pb "go.etcd.io/etcd/raft/v3/raftpb"

// ReadState为只读查询提供状态。
// 调用方有责任在获取
// 此状态之前先调用ReadIndex，调用方也有责任区分此
// 状态是否为其通过RequestCtx请求的状态，例如，给定一个唯一id为
// RequestCtx 
type ReadState struct {
	Index      uint64
	RequestCtx []byte
}

type readIndexStatus struct {
	req   pb.Message
	index uint64
	// 注意：这从不记录“false”，但是由于quorum的API，使用这个
	// 而不是map[uint64]结构{}更方便。VoteResult。如果
	// 这对性能足够敏感（值得怀疑），则quorum。VoteResult 
	// 可以更改为更接近CommittedIndex的API。
	acks map[uint64]bool
}

type readOnly struct {
	option           ReadOnlyOption
	pendingReadIndex map[string]*readIndexStatus
	readIndexQueue   []string
}

func newReadOnly(option ReadOnlyOption) *readOnly {
	return &readOnly{
		option:           option,
		pendingReadIndex: make(map[string]*readIndexStatus),
	}
}

// 将己提交位置（raftLog.committed）及MsgReadindex消息的相关信息记录到raft.readOnly 中
func (ro *readOnly) addRequest(index uint64, m pb.Message) {
	s := string(m.Entries[0].Data)	// 在Readindex消息的第一个记录中，记录了消息ID
	if _, ok := ro.pendingReadIndex[s]; ok {
		return
	}
	// 检测是否存在ID相同的MsgReadindex，如采存在，则不再记录该MsgReadI口dex请求
	// 创建MsgReadindex对应的readindexStatus实例，并记录到pendingReadindex中
	ro.pendingReadIndex[s] = &readIndexStatus{index: index, req: m, acks: make(map[uint64]bool)}
	ro.readIndexQueue = append(ro.readIndexQueue, s)	// 记录消息ID
}

// 统计指定的消息ID收到了多少MsgHeartbeatResp响应消息，从而确定在收到对应的MsgReadlndex消息的时候，
// 当前Leader节点是否为集群的Leader节点，
func (ro *readOnly) recvAck(id uint64, context []byte) map[uint64]bool {
	// 获取消息ID对应的readindexStatus实例
	rs, ok := ro.pendingReadIndex[string(context)]
	if !ok {
		return nil
	}

	rs.acks[id] = true
	return rs.acks
}

// 当收到的响应消息超过半数以上时，
// 会通过readOnly.advance（）方法清空指定消息ID及其之前的所有相关记录，
func (ro *readOnly) advance(m pb.Message) []*readIndexStatus {
	var (
		i     int
		found bool
	)

	ctx := string(m.Context)	// MsgHeartbeat消息对应的MsgReadI口dex消息ID
	rss := []*readIndexStatus{}

	for _, okctx := range ro.readIndexQueue {	// 边历readOnly中记录的消息ID
		i++
		rs, ok := ro.pendingReadIndex[okctx]	// 查找消息ID对应的readindexStatus实例，
		if !ok {
			panic("cannot find corresponding read state from pending map")
		}
		rss = append(rss, rs)	// 将readindexStatus实例保存到rss数组中
		if okctx == ctx {
			found = true	// 查找到指定的MsgReadindex消息ID，则设置found变量并跳出循环
			break
		}
	}

	if found {	// 查找到指定的MsgReadindex消息ID，则清空readOnly中所有在它之前的消息ID及相关内容
		ro.readIndexQueue = ro.readIndexQueue[i:]	// 清理readOnly.readindexQueue
		for _, rs := range rss {	// 清理readOnly.pendi呵Readindex
			delete(ro.pendingReadIndex, string(rs.req.Entries[0].Data))
		}
		return rss
	}

	return nil
}

// lastPendingRequestCtx返回只读结构中最后一个挂起的只读
// 请求的上下文。
func (ro *readOnly) lastPendingRequestCtx() string {
	if len(ro.readIndexQueue) == 0 {
		return ""
	}
	return ro.readIndexQueue[len(ro.readIndexQueue)-1]
}
